﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Collections;
using System.Reflection;
using System.Drawing.Imaging;
using ICSharpCode.SharpZipLib.Zip.Compression;
using System.Drawing;
using RungSoft.RungEBookMaker.Providers.DocFormater.Exceptions;

namespace RungSoft.RungEBookMaker.Providers.DocFormater.Umd
{
    public class UmdWriter
    {
        // Fields
        private BookBM mBook = null;
        private int[] mChaptersOff = null;
        private Random mRandom = new Random();
        private string mTotalContent = string.Empty;
        private int mTotalContentLen = 0;
        private ArrayList mTotalImageList = new ArrayList();

        private const int A_32K_BYTE = 0x8000;
        private const byte ACTUAL_WIDTH_S60_HORI = 0xcc;
        private const byte ACTUAL_WIDTH_S60_VERT = 0xac;
        private const byte ACTUAL_WIDTH_SP = 0xa6;
        private const int BASE_REFN_CHAP_OFF = 0x3000;
        private const int BASE_REFN_CHAP_STR = 0x4000;
        private const int BASE_REFN_CONTENT = 0x2000;
        private const int BASE_REFN_COVER = 0x1000;
        private const int BASE_REFN_PAGE_OFFSET = 0x7000;
        private const string BEYOND_END_FLAG = "\0";
        private const int BYTE_LEN = 1;
        private const byte COVER_TYPE_BMP = 0;
        private const byte COVER_TYPE_GIF = 2;
        private const byte COVER_TYPE_JPG = 1;
        private const int CURR_VERSION = 1;
        private const short DCTS_CMD_ID_AUTHOR = 3;
        private const short DCTS_CMD_ID_CDS_KEY = 240;
        private const short DCTS_CMD_ID_CHAP_OFF = 0x83;
        private const short DCTS_CMD_ID_CHAP_STR = 0x84;
        private const short DCTS_CMD_ID_CONTENT_ID = 10;
        private const short DCTS_CMD_ID_COVER_PAGE = 130;
        private const short DCTS_CMD_ID_DAY = 6;
        private const short DCTS_CMD_ID_FILE_LENGTH = 11;
        private const short DCTS_CMD_ID_FIXED_LEN = 12;
        private const short DCTS_CMD_ID_GENDER = 7;
        private const short DCTS_CMD_ID_LICENSE_KEY = 0xf1;
        private const short DCTS_CMD_ID_MONTH = 5;
        private const short DCTS_CMD_ID_PAGE_OFFSET = 0x87;
        private const short DCTS_CMD_ID_PUBLISHER = 8;
        private const short DCTS_CMD_ID_REF_CONTENT = 0x81;
        private const short DCTS_CMD_ID_TITLE = 2;
        private const short DCTS_CMD_ID_VENDOR = 9;
        private const short DCTS_CMD_ID_VERSION = 1;
        private const short DCTS_CMD_ID_YEAR = 4;
        private const byte FIXED_LINE_PER_PAGE_S60 = 50;
        private const byte FIXED_LINE_PER_PAGE_SP = 0x19;
        private List<WidthDataBM> mWidthDataS60 = new List<WidthDataBM>();
        private List<WidthDataBM> mWidthDataSP = new List<WidthDataBM>();

        // Methods
        internal UmdWriter(BookBM book)
        {
            this.mBook = book;
        }

        private byte CharWidth_S60(string @char, byte fontSize)
        {
            ushort c = @char[0];
            foreach (WidthDataBM wBM in this.mWidthDataS60)
            {
                if (((wBM.FontSize == fontSize) && (c >= wBM.RngFrom)) && (c <= wBM.RngTo))
                {
                    if (wBM.Length == 1)
                    {
                        return wBM.Data[0];
                    }
                    return wBM.Data[c - wBM.RngFrom];
                }
            }
            return fontSize;
        }

        private void ClearTempVariables()
        {
            this.mTotalContent = string.Empty;
            this.mTotalContentLen = 0;
            this.mChaptersOff = null;
            if (this.mWidthDataS60 == null)
            {
                this.mWidthDataS60 = new List<WidthDataBM>();
            }
            else
            {
                this.mWidthDataS60.Clear();
            }
            if (this.mWidthDataSP == null)
            {
                this.mWidthDataSP = new List<WidthDataBM>();
            }
            else
            {
                this.mWidthDataSP.Clear();
            }
        }

        /// <summary>
        /// 将文本压缩为数据块
        /// </summary>
        /// <param name="encoding">压缩使用的字符集</param>
        /// <param name="chapters">所有章节</param>
        /// <returns>压缩后的数据块集合</returns>
        private byte[][] CompressTxtContent(Encoding encoding, List<ChapterBM> chapters)
        {
            int totalContentLength = 0;
            string totalContent = string.Empty;
            byte[] bTotalContent;

            #region 填充以上三个变量
            for (int i = 0; i < chapters.Count; i++)
            {
                string content = chapters[i].Content.Replace("\r\n", "\u2029") + "\u2029";
                totalContent = totalContent + content;
                totalContentLength += content.Length * 2;
            }
            bTotalContent = new byte[totalContentLength];
            bTotalContent = Encoding.Unicode.GetBytes(totalContent); 
            #endregion

            // 数据块数目
            int blocksCount = (totalContentLength % 0x8000 == 0) ? totalContentLength / 0x8000 : totalContentLength / 0x8000 + 1;

            int inP = 0;// inner point: 填充Block时，Block内部递增的指针
            int outP = 0;// outer point: 当前正在处理的数据块序号
            byte[] input = new byte[0x8000];// 当前正在处理的数据块
            byte[][] bBlocks = new byte[blocksCount][];// 将所有数据拆解为固定大小的数据块
            Deflater deflater;

            for (int i = 0; i < bTotalContent.Length; i++)
            {
                input[inP] = bTotalContent[i];
                inP++;

                //　读完一个完整块后，处理之
                if ((inP == 0x8000) || (i == (bTotalContent.Length - 1)))
                {
                    byte[] output = new byte[0x8000];
                    deflater = new Deflater(Deflater.BEST_COMPRESSION, false);
                    if (deflater.IsNeedingInput)
                    {
                        deflater.SetInput(input, 0, input.Length);
                    }
                    deflater.Finish();
                    deflater.Deflate(output);
                    bBlocks[outP] = new byte[deflater.TotalOut];

                    deflater = new Deflater(Deflater.BEST_COMPRESSION, false);
                    if (deflater.IsNeedingInput)
                    {
                        deflater.SetInput(input, 0, input.Length);
                    }
                    deflater.Finish();
                    deflater.Deflate(bBlocks[outP]);

                    outP++;
                    input = null;
                    input = new byte[0x8000];
                    inP = 0;
                }
            }
            return bBlocks;
        }

        private bool GetPageOffsetS60(byte size, int actualWidth, out int[] result)
        {
            if ((size != 0x10) && (size != 12))
            {
                result = new int[0];
                return false;
            }
            this.GetWidthData_S60();
            List<int> pagesOff = new List<int>();
            pagesOff.Add(0);
            while (((int)pagesOff[pagesOff.Count - 1]) < this.mTotalContent.Length)
            {
                this.ParseOnePage(pagesOff.Count - 1, size, actualWidth, ref pagesOff, 1);
            }
            result = new int[pagesOff.Count];
            for (int i = 0; i < pagesOff.Count; i++)
            {
                result[i] = ((int)pagesOff[i]) * 2;
            }
            return true;
        }

        private bool GetPageOffsetSP(byte size, int actualWidth, out int[] result)
        {
            if ((size < 6) || (size > 0x10))
            {
                result = new int[0];
                return false;
            }
            List<int> pagesOff = new List<int>();
            pagesOff.Add(0);
            //while (((int)pagesOff[pagesOff.Count - 1]) < this._TotalContent.Length)
            while (((int)pagesOff[pagesOff.Count - 1]) < this.mTotalContent.Length)
            {
                //this.ParseOnePage((int)(pagesOff.Count - 1), size, actualWidth, ref pagesOff, 5);
                this.ParseOnePage(pagesOff.Count - 1, size, actualWidth, ref pagesOff, 5);
            }
            result = new int[pagesOff.Count];
            for (int i = 0; i < pagesOff.Count; i++)
            {
                result[i] = ((int)pagesOff[i]) * 2;
            }
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        private void GetWidthData_S60()
        {
            if (this.mWidthDataS60.Count == 0)
            {
                for (int i = 0; i < 2; i++)
                {
                    string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\FontWidthData\S60CHS." + ((i == 0) ? "S16" : "S12") + ".wdt";
                    if (File.Exists(path))
                    {
                        using (FileStream input = new FileStream(path, FileMode.Open, FileAccess.Read))
                        {
                            using (BinaryReader reader = new BinaryReader(input))
                            {
                                while (reader.BaseStream.Position < reader.BaseStream.Length)
                                {
                                    WidthDataBM data = new WidthDataBM
                                    {
                                        FontSize = (i == 0) ? ((byte)0x10) : ((byte)12),
                                        RngFrom = reader.ReadUInt16(),
                                        RngTo = reader.ReadUInt16(),
                                        Length = reader.ReadUInt16()
                                    };
                                    data.Data = reader.ReadBytes((int)data.Length);

                                    this.mWidthDataS60.Add(data);
                                }
                            }
                        }
                    }
                    else
                        throw (new FileNotFoundException("无法找到文字转换字典文件。"));
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void GetWidthData_SP()
        {
            if (this.mWidthDataSP.Count == 0)
            {
                for (int i = 6; i < 0x10; i++)
                {
                    string path = string.Concat(new object[] { Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @"\FontWidthData\sunfon.s", i, ".wdt" });
                    if (File.Exists(path))
                    {
                        using (FileStream input = new FileStream(path, FileMode.Open, FileAccess.Read))
                        {
                            using (BinaryReader reader = new BinaryReader(input))
                            {
                                while (reader.BaseStream.Position < reader.BaseStream.Length)
                                {
                                    WidthDataBM data = new WidthDataBM
                                    {
                                        FontSize = (byte)i,
                                        RngFrom = reader.ReadUInt16(),
                                        RngTo = reader.ReadUInt16(),
                                        Length = reader.ReadUInt16()
                                    };
                                    data.Data = reader.ReadBytes((int)data.Length);

                                    this.mWidthDataSP.Add(data);
                                }
                            }
                        }
                    }
                    else
                        throw (new FileNotFoundException("无法找到文字转换字典文件。"));
                }
            }
        }

        public string MakeImageBook(BinaryWriter writer)
        {
            this.WriteChaptersOff(writer);
            this.WriteChapterTitles(writer);
            if (this.mBook.Formate[1] == 2)
            {
                this.WriteChapterImageContent(writer, 14);
            }
            else if (this.mBook.Formate[1] == 3)
            {
                this.WriteChapterImageContent(writer, 15);
            }
            if (this.mBook.Cover != null)
            {
                this.WriteBookCover(writer);
            }
            writer.Write('#');
            writer.Write((short)0xf1);
            writer.Write((byte)0);
            writer.Write((byte)0x15);
            writer.Write(Encoding.ASCII.GetBytes("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"));
            return null;
        }

        public void MakeTxtBook(BinaryWriter writer)
        {
            writer.Write('#');
            writer.Write((short)11);
            writer.Write((byte)0);
            writer.Write((byte)9);
            writer.Write(this.mTotalContentLen);

            // 写入各章节偏移量
            this.WriteChaptersOff(writer);

            // 写入各章节标题
            this.WriteChapterTitles(writer);

            if (this.mBook.Formate[0] == 1)
                this.WriteChapterTxtContents(writer);

            if (this.mBook.Cover != null)
                this.WriteBookCover(writer);

            int[] numArray;
            this.GetPageOffsetS60(0x10, 0xcc, out numArray);
            this.WritePageOffset(0x10, 0xd0, ref numArray, writer, 1);
            this.GetPageOffsetS60(0x10, 0xac, out numArray);
            this.WritePageOffset(0x10, 0xb0, ref numArray, writer, 1);
            this.GetPageOffsetS60(12, 0xcc, out numArray);
            this.WritePageOffset(12, 0xd0, ref numArray, writer, 1);
            this.GetPageOffsetS60(12, 0xac, out numArray);
            this.WritePageOffset(12, 0xb0, ref numArray, writer, 1);
            this.GetPageOffsetSP(10, 0xa6, out numArray);
            this.WritePageOffset(10, 0xa6, ref numArray, writer, 5);
        }

        private void ParseOnePage(int pageNumber, byte fontSize, int screenWidth, ref List<int> pagesOff, int PID)
        {
            if (pageNumber < pagesOff.Count)
            {
                int num = (int)pagesOff[(int)pageNumber];
                int num2 = 0;
                string str = string.Empty;
                ArrayList list = new ArrayList();
                byte num3 = (PID == 1) ? ((byte)50) : ((byte)0x19);
                for (byte i = 0; i < num3; i = (byte)(i + 1))
                {
                    str = string.Empty;
                    string str2 = string.Empty;
                    byte num5 = 0;
                Label_0053:
                    if (num < this.mTotalContent.Length)
                    {
                        str2 = this.mTotalContent.Substring((int)num, 1);
                    }
                    else
                    {
                        str2 = "\0";
                    }
                    switch (str2)
                    {
                        case "\t":
                        case "\0":
                            str2 = "　";
                            break;
                    }
                    byte num6 = this.CharWidth_S60(str2, fontSize);
                    if (str2 == "\u2029")
                    {
                        num6 = 0;
                    }
                    if ((num6 + num5) <= screenWidth)
                    {
                        num5 = (byte)(num5 + num6);
                        num++;
                        if (str2 != "\u2029")
                        {
                            str = str + str2;
                            goto Label_0053;
                        }
                    }
                    if (str2 != "\u2029")
                    {
                        list.Add(str.Length);
                    }
                    else
                    {
                        list.Add(str.Length + 1);
                    }
                    num2 += (int)list[i];
                    if (i == ((byte)(num3 - 1)))
                    {
                        if ((num < this.mTotalContent.Length) && (num > ((int)pagesOff[pagesOff.Count - 1])))
                        {
                            pagesOff.Add(((int)num2) + ((int)pagesOff[pagesOff.Count - 1]));
                        }
                        if (num >= this.mTotalContent.Length)
                        {
                            pagesOff.Add((int)this.mTotalContent.Length);
                        }
                    }
                }
            }
        }

        private bool Precheck()
        {
            if ((this.mBook.Title == null) || (this.mBook.Title.Length < 1))
            {
                throw (new MetaInfoNullException("标题不能为空！"));
            }
            if ((this.mBook.Author == null) || (this.mBook.Author.Length < 1))
            {
                throw (new MetaInfoNullException("作者不能为空！"));
            }
            if ((this.mBook.Chapters == null) || (this.mBook.Chapters.Count < 1))
            {
                throw (new MetaInfoNullException("内容数量不能小于0！"));
            }
            if ((this.mBook.FilePath == null) || (this.mBook.FilePath.Length < 1))
            {
                throw (new FileNotFoundException("保存文件的路径不不正确！"));
            }
            if (File.Exists(this.mBook.FilePath))
            {
                File.Delete(this.mBook.FilePath);
            }
            return true;
        }

        /// <summary>
        /// 准备制作前的统计数据。
        /// 例如：所有的正文内容、正文总长度、章节偏移量。
        /// </summary>
        private void Prepare()
        {
            ChapterBM chapter = null;
            int[] chapterOffset = new int[this.mBook.Chapters.Count];
            int contentLength = 0;
            string currContent = null;
            string totalContent = string.Empty;

            for (int i = 0; i < this.mBook.Chapters.Count; i++)
            {
                chapter = this.mBook.Chapters[i];
                chapterOffset[i] = contentLength;
                if (this.mBook.Formate[1] == 2)// 漫画
                {
                    this.mTotalImageList.AddRange(chapter.ImageList);
                    contentLength += chapter.ImageList.Count;
                }
                else if (this.mBook.Formate[1] == 1)// 小说
                {
                    currContent = chapter.Content.Replace("\r\n", "\u2029") + "\u2029";
                    totalContent += currContent;
                    contentLength += currContent.Length * 2;
                }
            }

            this.mTotalContent = totalContent;
            this.mTotalContentLen = contentLength;
            this.mChaptersOff = chapterOffset;
        }

        public void Write()
        {
            if (this.Precheck() == true)
            {
                this.ClearTempVariables();
                this.Prepare();

                using (FileStream output = new FileStream(this.mBook.FilePath, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    using (BinaryWriter writer = new BinaryWriter(output))
                    {
                        short randNum = (short)(this.mRandom.Next(0x401, 0x7fff) % 0xffff);
                        writer.Write((uint)0xde9a9b89);
                        writer.Write('#');
                        writer.Write((short)1);
                        writer.Write((byte)0);
                        writer.Write((byte)8);
                        writer.Write(this.mBook.Formate[1]);
                        writer.Write(randNum);

                        this.WriteBookProperties(writer);// 写入书籍的元数据

                        if (this.mBook.Formate[1] == 1)
                        {
                            this.MakeTxtBook(writer);
                        }
                        else if (this.mBook.Formate[1] == 2)
                        {
                            this.MakeImageBook(writer);
                        }
                        writer.Write('#');
                        writer.Write((short)12);
                        writer.Write((byte)1);
                        writer.Write((byte)9);
                        writer.Write((int)(((int)writer.BaseStream.Position) + 4));
                    }
                }
            }
        }

        /// <summary>
        /// 将书籍的封面图片写入流。
        /// </summary>
        /// <param name="writer">待写入数据的二进制流</param>
        private void WriteBookCover(BinaryWriter writer)
        {
            if (this.mBook.Cover == null) throw (new NullReferenceException("书籍封面为空引用，无法写入到文件。"));
            // 获取随机值
            int random = (int)(0x1000 + this.mRandom.Next(0xfff));

            // 图片字节流
            byte[] buffImage = null;

            #region 获取图片字节流
            using (MemoryStream stream = new MemoryStream())
            {
                this.mBook.Cover.Save(stream, ImageFormat.Gif);
                stream.Seek(0L, SeekOrigin.Begin);
                buffImage = new byte[stream.Length];
                stream.Read(buffImage, 0, buffImage.Length);
            } 
            #endregion

            writer.Write('#');
            writer.Write((short)130);
            writer.Write((byte)1);
            writer.Write((byte)10);
            writer.Write((byte)1);
            writer.Write(random);
            writer.Write('$');
            writer.Write(random);
            writer.Write((int)(9 + buffImage.Length));
            writer.Write(buffImage);
            buffImage = null;
        }

        /// <summary>
        /// 将书籍的元数据集合写入流。
        /// </summary>
        /// <param name="writer">待写入数据的二进制流</param>
        private void WriteBookProperties(BinaryWriter writer)
        {
            Encoding bookEncoding = this.mBook.Encoding;

            string year     = this.mBook.Date.Year.ToString();
            string month    = this.mBook.Date.Month.ToString();
            string day      = this.mBook.Date.Day.ToString();

            this.WriteBookProperty(writer, 2, this.mBook.Title);
            this.WriteBookProperty(writer, 3, this.mBook.Author);
            this.WriteBookProperty(writer, 4, year);
            this.WriteBookProperty(writer, 5, month);
            this.WriteBookProperty(writer, 6, day);
            if ((this.mBook.Kind != null) && (this.mBook.Kind.Length > 0))
            {
                this.WriteBookProperty(writer, 7, this.mBook.Kind);
            }
            if ((this.mBook.Publisher != null) && (this.mBook.Publisher.Length > 0))
            {
                this.WriteBookProperty(writer, 8, this.mBook.Publisher);
            }
            if ((this.mBook.Vendor != null) && (this.mBook.Vendor.Length > 0))
            {
                this.WriteBookProperty(writer, 9, this.mBook.Vendor);
            }
        }

        /// <summary>
        /// 将指定的书籍元数据键值写入流。
        /// </summary>
        /// <param name="writer">待写入数据的二进制流</param>
        /// <param name="pType">元数据键</param>
        /// <param name="pValue">元数据值</param>
        private void WriteBookProperty(BinaryWriter writer, int pType, string pValue)
        {
            writer.Write('#');
            writer.Write((short)pType);
            writer.Write((byte)0);
            writer.Write((byte)(5 + (pValue.Length * 2)));
            writer.Write(this.mBook.Encoding.GetBytes(pValue));
        }

        private void WriteChapterImageContent(BinaryWriter writer, int SegType)
        {
            int num = this.mRandom.Next(0x5f5e101, 0x3b9aca00);
            int[] numArray = new int[this.mTotalImageList.Count];
            int num2 = 0;
            if (this.mTotalImageList.Count > 1)
            {
                num2 = (int)this.mRandom.Next(0, this.mTotalImageList.Count - 1);
            }
            writer.Write('#');
            writer.Write((short)SegType);
            writer.Write((byte)0);
            writer.Write((byte)6);
            writer.Write((byte)1);
            byte[] buffer = null;
            for (int i = 0; i < this.mTotalImageList.Count; i++)
            {
                Image image = (Image)this.mTotalImageList[i];
                MemoryStream stream = new MemoryStream();
                image.Save(stream, ImageFormat.Jpeg);
                stream.Seek(0L, SeekOrigin.Begin);
                buffer = new byte[stream.Length];
                stream.Read(buffer, 0, buffer.Length);
                stream.Close();
                stream = null;
                numArray[i] = (int)(this.mRandom.Next(1, 0xfffffff) * -1);
                writer.Write('$');
                writer.Write(numArray[i]);
                writer.Write((int)(9 + buffer.Length));
                writer.Write(buffer);
                if (num2 == i)
                {
                    writer.Write('#');
                    writer.Write((short)10);
                    writer.Write((byte)0);
                    writer.Write((byte)9);
                    writer.Write(num);
                }
            }
            num2 = (int)(0x2000 + this.mRandom.Next(0xfff));
            writer.Write('#');
            writer.Write((short)0x81);
            writer.Write((byte)1);
            writer.Write((byte)9);
            writer.Write(num2);
            writer.Write('$');
            writer.Write(num2);
            writer.Write((int)(9 + (numArray.Length * 4)));
            for (int j = 0; j < numArray.Length; j++)
            {
                writer.Write(numArray[j]);
            }
        }

        /// <summary>
        /// 将各章节偏移量写入流
        /// </summary>
        /// <param name="writer">待写入数据的流</param>
        private void WriteChaptersOff(BinaryWriter writer)
        {
            int random = (int)(0x3000 + this.mRandom.Next(0xfff));
            writer.Write('#');
            writer.Write((short)0x83);
            writer.Write((byte)1);
            writer.Write((byte)9);
            writer.Write(random);
            writer.Write('$');
            writer.Write(random);
            writer.Write(9 + (this.mChaptersOff.Length * 4));

            // 依次写入各章节的偏移量
            for (int i = 0; i < this.mChaptersOff.Length; i++)
            {
                writer.Write(this.mChaptersOff[i]);
            }
        }

        /// <summary>
        /// 将各章节标题写入流
        /// </summary>
        /// <param name="writer">待写入数据的流</param>
        private void WriteChapterTitles(BinaryWriter writer)
        {
            int chaptersLength = 0;
            int random = (int)(0x4000 + this.mRandom.Next(0xfff));

            // 计算章节标题总长度
            foreach (ChapterBM cBM in this.mBook.Chapters)
            {
                chaptersLength += (cBM.Title.Length * 2) + 1;
            }

            writer.Write('#');
            writer.Write((short)0x84);
            writer.Write((byte)1);
            writer.Write((byte)9);
            writer.Write(random);
            writer.Write('$');
            writer.Write(random);
            writer.Write((int)(9 + chaptersLength));

            // 依次写入各章节的标题
            foreach (ChapterBM chapter2 in this.mBook.Chapters)
            {
                writer.Write((byte)(chapter2.Title.Length * 2));
                writer.Write(this.mBook.Encoding.GetBytes(chapter2.Title));
            }
        }

        /// <summary>
        /// 将所有章节写入到流
        /// </summary>
        /// <param name="writer">待写入数据的流</param>
        private void WriteChapterTxtContents(BinaryWriter writer)
        {
            // 压缩后的数据
            byte[][] bBlocks = this.CompressTxtContent(this.mBook.Encoding, this.mBook.Chapters);
            int random1 = 0;
            int random2 = 0;
            if (bBlocks.Length > 1)
            {
                random1 = this.mRandom.Next(0, bBlocks.Length - 1);
                random2 = this.mRandom.Next(0, bBlocks.Length - 1);
            }
            // 为每个数据块定制的随机数的集合
            int[] blockRandom = new int[bBlocks.Length];

            for (int i = 0; i < bBlocks.Length; i++)
            {
                // 生成该数据块的随机数
                blockRandom[i] = (int)(this.mRandom.Next(1, 0xfffffff) * -1);

                writer.Write('$');
                writer.Write(blockRandom[i]);
                writer.Write((int)(9 + bBlocks[i].Length));
                writer.Write(bBlocks[i]);// 将数据块写入到流

                #region 写入填充物（具有随机性，参见UMD格式说明）
                if (i == random1)
                {
                    writer.Write('#');
                    writer.Write((short)0xf1);
                    writer.Write((byte)0);
                    writer.Write((byte)0x15);
                    writer.Write(Encoding.ASCII.GetBytes("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"));
                }
                if (i == random2)
                {
                    int r = this.mRandom.Next(0x5f5e101, 0x3b9aca00);

                    writer.Write('#');
                    writer.Write((short)10);
                    writer.Write((byte)0);
                    writer.Write((byte)9);
                    writer.Write(r);
                } 
                #endregion
            }

            int random = (int)(0x2000 + this.mRandom.Next(0xfff));
            writer.Write('#');
            writer.Write((short)0x81);
            writer.Write((byte)1);
            writer.Write((byte)9);
            writer.Write(random);
            writer.Write('$');
            writer.Write(random);
            writer.Write((int)(9 + (blockRandom.Length * 4)));

            // 写入所有的随机数
            for (int i = 0; i < blockRandom.Length; i++)
            {
                writer.Write(blockRandom[i]);
            }
        }

        private void WritePageOffset(byte fontSize, byte screenWidth, ref int[] data, BinaryWriter writer, byte PID)
        {
            Random random = new Random();
            int num = (int)(0x7000 + random.Next(0xfff));
            writer.Write('#');
            writer.Write((short)0x87);
            writer.Write(PID);
            writer.Write((byte)11);
            writer.Write(fontSize);
            writer.Write(screenWidth);
            writer.Write(num);
            writer.Write('$');
            writer.Write(num);
            writer.Write((int)(9 + (data.Length * 4)));
            foreach (int num2 in data)
            {
                writer.Write(num2);
            }
        }

        // Nested Types
        [StructLayout(LayoutKind.Sequential)]
        private struct WidthDataBM
        {
            public byte FontSize;
            public ushort RngFrom;
            public ushort RngTo;
            public int Length;
            public byte[] Data;
        }
    }
}
